/*
 * Created on 26/apr/07
 *
 * To change the template for this generated file go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
package it.unibo.deis.lia.networkInterface;

import it.unibo.deis.lia.location.Location;
import it.unibo.deis.lia.location.RemoteBluetooth;

import java.io.*;
//import java.net.*;
import java.util.*;

public class GenericNetworkInterface {
	
	protected static final String interfacePrefixes[]={"eth","wlan","ra0"};
	protected static final String DHCPDAEMON = "dhcpd3";
	protected static final String DHCLIENT="dhclient3";
	protected static final String dhcpConfigTemplateFile = "./lib/dhcpd.conf.template";
	
	protected String name=null;
	protected String description=null;
	protected String hardwareAddress=null;
	protected int windowsNumber=-100;
    
	
	//Mac o essid da evitare per collegarsi comune alle varie interfacce si poteva usare
	//un solo vettore di location ma le ho tenute separte tra wifi e bluetooth
	protected static Vector<Location> notAllowedESSIDs;
	protected static Vector<RemoteBluetooth> notAllowedRemoteBluetooth;
	
	protected GenericNetworkInterface(){
		//
	}
	
	protected GenericNetworkInterface(String name,String description,String hardwareAddress,int number){
		if(notAllowedESSIDs==null)
			notAllowedESSIDs=new Vector<Location>();
		if(notAllowedRemoteBluetooth==null)
			notAllowedRemoteBluetooth=new Vector<RemoteBluetooth>();
		this.name=name;
		this.description=description;
		this.hardwareAddress=hardwareAddress;
		this.windowsNumber=number;
	}
	
	public String getName(){
		return name;
	}
	public String getDescription(){
		return description;
	}
	public String getHardwareAddress(){
		return hardwareAddress;
	}
	/*public int getNumber(){
		return number;
	}*/
	
	public String toString(){
		return "Name="+name+ 
		" HardwareAddress="+hardwareAddress+
		" Description="+description;
		//+" Number="+number;
	}
	
	public final static Vector<GenericNetworkInterface> getInterfaces() throws Exception{
		
		Vector<GenericNetworkInterface> interfaces;//=new Vector<GenericNetworkInterface>();
		String os = System.getProperty("os.name");
				
		if(os.startsWith("Windows")) {
			interfaces=getInterfacesWindows();
		} else if(os.startsWith("Linux")) {
			interfaces=getInterfacesLinux();
		} else{
			throw new Exception("Not supported Operating System: " + os);
		}
		
		/* To be tested: sometimes linux does not provide IEEE 802.11 interfaces
		Enumeration<NetworkInterface> interfacesJava=NetworkInterface.getNetworkInterfaces();
		//int index=0;
		while(interfacesJava.hasMoreElements()){
			NetworkInterface anInterface=interfacesJava.nextElement();
			if(!anInterface.isLoopback() && anInterface.getHardwareAddress()!=null){
				String name=anInterface.getName();
				String displayName=anInterface.getDisplayName();
				String mac=byteArrayToHexString(anInterface.getHardwareAddress());
				int deviceNumber=-100;
				if(os.startsWith("Windows")) {
					deviceNumber=getWindowsNumber(displayName);
				}
				//System.out.println("GenericNetworkInterface: deviceNumber "+deviceNumber);
				interfaces.addElement(new GenericNetworkInterface(name,displayName,mac,deviceNumber));
				//index++;
			}
		}
		*/
		
		return interfaces;
	}
	/*private static String byteArrayToHexString(byte in[]) {
	    byte ch = 0x00;
	    int i = 0; 
	    if (in == null || in.length <= 0)
	        return null;

	    String pseudo[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 
	    		"A", "B", "C", "D", "E", "F"};
	    
	    StringBuffer out = new StringBuffer(in.length * 2);
	    while (i < in.length) {
	        ch = (byte) (in[i] & 0xF0); // Strip off high nibble
	        ch = (byte) (ch >>> 4);
	        // shift the bits down
	        ch = (byte) (ch & 0x0F);    
	        // must do this is high order bit is on!
	        out.append(pseudo[ (int) ch]); // convert the nibble to a String Character
	        ch = (byte) (in[i] & 0x0F); // Strip off low nibble 
	        out.append(pseudo[ (int) ch]); // convert the nibble to a String Character
	        i++;
	    }
	    String rslt = new String(out);
	    return rslt;
	}*/
	
	/**
	 * Metodo che prepara il file di configurazione del server DHCP.
	 * A partire da un template nel file di configurazione, crea il file finale
	 * aggiungendo le informazioni sulla sottorete che il server DHCP dovra` gestire
	 */
	protected void makeDhcpdConf(String connectorRoleIP,String dhcpConfigFile) throws FileNotFoundException, IOException {
		BufferedReader reader=new BufferedReader(new FileReader(dhcpConfigTemplateFile));
		BufferedWriter writer=new BufferedWriter(new FileWriter(dhcpConfigFile));
		String line;
		//copia del file template nel file di configurazione
		while((line=reader.readLine())!=null){
			writer.write(line);
			writer.newLine();
		}
		reader.close();
		
		//aggiunta della configurazione della rete
		String ipFields[]=connectorRoleIP.split("[.]");
		String networkAddress=ipFields[0]+"."+ipFields[1]+"."+ipFields[2];
		writer.write("subnet "+networkAddress+".0 netmask 255.255.255.0 {\n"+
			"\trange "+networkAddress+".10 "+networkAddress+".19;\n"+
			"\toption domain-name-servers 137.204.58.1, 137.204.59.1;\n"+
			"\toption subnet-mask 255.255.255.0;\n"+
			"\toption routers "+connectorRoleIP+";\n"+
			"}");
		writer.close();
	}
	protected void makeDhcpdConf(String connectorRoleIP,String dhcpConfigFile, String connectorRoleMac) throws FileNotFoundException, IOException {
		BufferedReader reader=new BufferedReader(new FileReader(dhcpConfigTemplateFile));
		BufferedWriter writer=new BufferedWriter(new FileWriter(dhcpConfigFile));
		String line;
		//copia del file template nel file di configurazione
		while((line=reader.readLine())!=null){
			writer.write(line);
			writer.newLine();
		}
		reader.close();
		
		//aggiunta della configurazione della rete
		String ipFields[]=connectorRoleIP.split("[.]");
		String networkAddress=ipFields[0]+"."+ipFields[1]+"."+ipFields[2];
		writer.write("subnet "+networkAddress+".0 netmask 255.255.255.0 {\n"+
			"\trange "+networkAddress+".10 "+networkAddress+".19;\n"+
			"\toption domain-name-servers 137.204.58.1, 137.204.59.1;\n"+
			"\toption subnet-mask 255.255.255.0;\n"+
			"\toption routers "+connectorRoleIP+";\n"+
			"}\n\n\n");
		writer.write("host local {\n" +
				"\thardware ethernet "+connectorRoleMac+";\n" +
				"\tfixed-address "+networkAddress+".1;\n" +
			    "}");
		
		writer.close();
	}
	
	private static boolean interestingInterface(String string){
		for(int i=0;i<interfacePrefixes.length;i++){
			if(string.contains(interfacePrefixes[i])){
				return true;
			}
		}
		return false;
	}
	
	private final static Vector<GenericNetworkInterface> getInterfacesLinux() throws IOException {
		//System.out.println("GenericNetworkInterface linuxRunIfConfigCommand: init");
		
		Vector<GenericNetworkInterface> interfaces = new Vector<GenericNetworkInterface>();
		// ---------
		// ifconfig
		// ---------
		/**/
		
		Process p=null;
		try{
			p = Runtime.getRuntime().exec("ifconfig");
		}
		catch(Exception e){
			e.printStackTrace();
			System.err.println("GenericNetworkInterface: The program must be executed with administrator rights!!!");
			System.exit(-1);
		}
		InputStream stdoutStream = new BufferedInputStream(p.getInputStream());
 
		StringBuffer buffer= new StringBuffer();
		int c = stdoutStream.read();
		while(c!=-1){
			buffer.append((char)c);
			c = stdoutStream.read();
		}
		stdoutStream.close();
		
		String ifconfigOutput = buffer.toString();
		//int pos = 0;
		
		//controllo che nell'output di ifconfig ci siano interfacce di interesse
		if(interestingInterface(ifconfigOutput)){
			//pos = ifconfigOutput.indexOf("eth");
			//ifconfigOutput = ifconfigOutput.substring(pos);
			
			String macAddressCandidate = null;
			String description = null;
			String []lines=ifconfigOutput.split("\n");
			try {
				for(int i=0;i<lines.length;i++)	{
					if(interestingInterface(lines[i])){
						description=(lines[i].split(" "))[0];
					}
					if(description!=null && lines[i].contains("HWaddr")){
						String[] tokens=lines[i].split("[ ]");
						for(int j=0;j<tokens.length;j++){
							if(tokens[j].equals("HWaddr")){
								macAddressCandidate=tokens[j+1];
							}
						}
						
						//TODO: rischio di collisione tra indici di interfacce con prefissi diversi...
						int j=0;
						while(!Character.isDigit(description.charAt(j)))
							j++;
						int n = Integer.parseInt(description.substring(j));
						interfaces.addElement(new GenericNetworkInterface(description,description,macAddressCandidate,n));

						description=null;
						macAddressCandidate=null;
					}
				}
			}
			catch(Exception ex) {
				ex.printStackTrace();	
			}
		}/**/
		
		// ----------
		// hciconfig
		// ----------
		Process p2 = Runtime.getRuntime().exec("hciconfig");
		InputStream stdoutStream2 = new BufferedInputStream(p2.getInputStream());

		StringBuffer buffer2= new StringBuffer();
		int c2 = stdoutStream2.read();
		while(c2!=-1){
			buffer2.append((char)c2);
			c2 = stdoutStream2.read();
		}
		stdoutStream2.close();

		String hciconfigOutput = buffer2.toString();
		int pos2 = 0;
		if(hciconfigOutput.contains("hci"))
		{
			pos2 = hciconfigOutput.indexOf("hci");
			hciconfigOutput = hciconfigOutput.substring(pos2);

			String macAddressCandidate = null;
			String description = null;
			String []lines=hciconfigOutput.split("\n");
			try {
				for(int i=0;i<lines.length;i++){
					//System.out.println("lines["+i+"] "+lines[i]);
					if(lines[i].contains("hci")){
						description=(lines[i].split("[ :]"))[0];
						//System.out.println("description "+description);
					}
					if(lines[i].contains("Address:")){
						String[] tokens=lines[i].split("[ ]");
						for(int j=0;j<tokens.length;j++){
							if(tokens[j].equals("Address:")){
								macAddressCandidate=tokens[j+1];
							}
						}
						int n = Integer.parseInt(description.substring(3));
						interfaces.addElement(new GenericNetworkInterface(description,description,macAddressCandidate,n));
						//System.out.println("macAddressCandidate "+macAddressCandidate);
						//System.out.println("n "+n);
						description=null;
						macAddressCandidate=null;
					}
				}
			}
			catch(Exception ex) {
				ex.printStackTrace();	
			}
		}
		
		return interfaces;
	}
	
	private final static Vector<GenericNetworkInterface> getInterfacesWindows() throws IOException {
		//System.out.println("NetworkInterface80211 windowsRunIpConfigCommand: init");
		
		Vector<GenericNetworkInterface> interfaces = new Vector<GenericNetworkInterface>();
		// ----------
		// ipconfig
		// ----------
		/**/Process p = Runtime.getRuntime().exec("ipconfig /all");
		InputStream stdoutStream = new BufferedInputStream(p.getInputStream());
 
		StringBuffer buffer= new StringBuffer();
		int c = stdoutStream.read();
		while (c!=-1) {
			buffer.append((char)c);
			c = stdoutStream.read();
		}
		String ipconfigOutput = buffer.toString();
 
		//StringTokenizer tokenizer = new StringTokenizer(ipconfigOutput, "\n");
		String macAddressCandidate = null;
		String description = null;
		String name = null;
		String[]lines;
		
		try{
			//while(tokenizer.hasMoreTokens()){
			lines=ipconfigOutput.split("\n");
			for(int i=0;i<lines.length;i++){
				//String line = tokenizer.nextToken().trim();
				//if(lines[i].startsWith("\t")){
				//	System.out.println("TAB! lines["+i+"] "+lines[i]);
				//}
				if(!lines[i].startsWith("   ") && !lines[i].startsWith("\n") && !lines[i].startsWith("\r")){
					//System.out.println("  ! lines["+i+"] >"+lines[i]+"<");
					name=lines[i].substring(0, lines[i].length()-2);
				}
				if(lines[i].contains("Descrizione") || lines[i].contains("Description")){
					int descrPosition = lines[i].indexOf(":");
					description = lines[i].substring(descrPosition + 1).trim();
				}
				if(lines[i].contains("Indirizzo fisico") || lines[i].contains("Physical Address")){
					int macAddressPosition = lines[i].indexOf(":");	
					macAddressCandidate = lines[i].substring(macAddressPosition + 1).trim();
				}
				
				if(description!=null && macAddressCandidate!=null){
					int deviceNumber=getWindowsNumber(description);
					//boolean isWireless=isWirelessWindows(deviceNumber);
					//if(isWireless)
						interfaces.addElement(new GenericNetworkInterface(name,description,macAddressCandidate,deviceNumber));
					macAddressCandidate = null;
					description = null;
					name=null;
				}
			} 
			
		}
		catch (Exception ex){
			ex.printStackTrace();
		}/**/
		
		// ----------
		// BTcommand
		// ----------
		Process p2 = Runtime.getRuntime().exec("./lib/BTcommand devices");
//		System.out.println("OK");
		InputStream stdoutStream2 = new BufferedInputStream(p2.getInputStream());

		StringBuffer buffer2= new StringBuffer();
		int c2 = stdoutStream2.read();
		while(c2!=-1){
			buffer2.append((char)c2);
			c2 = stdoutStream2.read();
		}
		stdoutStream2.close();

		String btcommandOutput = buffer2.toString();
		//String[] 
		lines=btcommandOutput.split("\n");
		try {
			int n=0;
			for(int i=0;i<lines.length;i++){
				//se non e' la riga dell'errore
				if(! (lines[i].contains("Error:"))){
					String[] tokens=lines[i].split("\t");
					//System.out.println(tokens[0]+","+tokens[1]);
					if (tokens.length == 3){
						interfaces.addElement(new GenericNetworkInterface("",tokens[1].trim(),tokens[2].trim(),n));
						n++;
					}
				}
			}
		}
		catch(Exception ex) {
			ex.printStackTrace();	
		}
		
		return interfaces;
	}
	
	private final static int getWindowsNumber(String descrizione)throws Exception{
		System.out.println("getWindowsNumber descrizione: "+descrizione);
		Process p = Runtime.getRuntime().exec("./lib/80211UtilWindows.exe getDeviceList");
		InputStream stdoutStream = new BufferedInputStream(p.getInputStream());
 
		StringBuffer buffer= new StringBuffer();
		int c = stdoutStream.read();
		while(c!=-1){
			buffer.append((char)c);
			c = stdoutStream.read();
		}
		String outputText = buffer.toString();
		stdoutStream.close();
		
		int number=-2;
		String[] lines=outputText.split("\n");
		for(int i=0;i<lines.length;i++){
			if(lines[i].contains("Device number")){
				number=Integer.parseInt(lines[i].split("[ ]")[2]);
			}
			if(lines[i].contains("Description"))
			{
				String aDescr=lines[i].split(" ",2)[1];
				System.out.println("getWindowsNumber aDescr: "+aDescr);
				if(aDescr.contains(descrizione) || descrizione.contains(aDescr)){
					System.out.println("getWindowsNumber number: "+number);
					return number;
				}
			}
		}
					
		return number;
	}
	
}
